/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ru.ssau.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jdom.Element;
import ru.ssau.entities.AuthorizationInfo;
import ru.ssau.entities.Message;
import ru.ssau.entities.User;
import ru.ssau.lib.InvalidValueException;
import ru.ssau.lib.StringIntegerPair;
import ru.ssau.model.storing.DataAccessException;
import ru.ssau.model.storing.ForumDataAccessObject;
import ru.ssau.model.storing.factories.ForumDAOFactory;
import ru.ssau.model.storing.factories.ForumDatabaseDAOFactory;
import ru.ssau.model.storing.factories.ForumMapDAOFactory;
import ru.ssau.utils.XMLHelper;
import ru.ssau.utils.StringUtils;


public class ForumManagerServlet extends HttpServlet {

    

    private ForumDAOFactory daoFactory;

    @Override
    public void init() {
        initDAOFactory();
    }

    @Override
    public void destroy() {
        try {
            daoFactory.getDAO().closeStore();
        } catch(DataAccessException e) {

        }
    }

    private void initDAOFactory() {
        try {
            java.io.InputStream resIn = this.getClass().getResourceAsStream("/resources/store.properties");
            java.util.Properties settings = new java.util.Properties();
            settings.load(resIn);

            //По умолчанию в качестве хранилища данных используются карты
            String daoType = settings.getProperty("storetype", "map");
            System.out.println("type: "+daoType);
            if(daoType.equalsIgnoreCase("database")) {
                daoFactory = new ForumDatabaseDAOFactory(settings);
            } else {
                daoFactory = new ForumMapDAOFactory();
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    private void initStore() {
        try {
            ForumDataAccessObject forumDAO = daoFactory.getDAO();
            User u = new User();
            u.setAuthorizationInfo(new AuthorizationInfo(1, 1, "some login", "some pass"));
            u.setId(1); u.setInfo("user 1 info"); u.setName("user 1"); u.setRegistrationDate(new Date());
            forumDAO.addUser(u);
            User u2 = new User();
            u2.setAuthorizationInfo(new AuthorizationInfo(2, 2, "asdf", "123"));
            u2.setId(2); u2.setInfo("eeeeee"); u2.setName("kaka"); u2.setRegistrationDate(new Date());
            forumDAO.addUser(u2);
            ru.ssau.entities.Thread t = new ru.ssau.entities.Thread(1, "thread 1");
            forumDAO.addThread(t);
            ru.ssau.entities.Thread t2 = new ru.ssau.entities.Thread(2, "thread 222");
            forumDAO.addThread(t2);
            Message m = new Message(1, "capt", "some text", 1, 1, new Date());
            forumDAO.addMessage(m);
            Message m2 = new Message(2, "ddd", "mes txt", 2, 1, new Date());
            forumDAO.addMessage(m2);
            Message m3 = new Message(3, "aqsdefghy", "qqq", 2, 2, new Date());
            forumDAO.addMessage(m3);

        } catch(Exception e) {

        }
    }

    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out =  response.getWriter();
        HttpSession session = request.getSession();
        //Атрибут message - сообщение об ошибке, которое выводится на отдельной
        //jsp-странице, если в коде сервлета возникнет исключение
        session.setAttribute("message", null);

        try {
            ForumDataAccessObject forumDAO = daoFactory.getDAO();
            //Зпрос по умолчанию - запрос всех данных
            String action = request.getParameter(Constants.ACTION);
            if(StringUtils.isEmpty(action)) {
                action = Constants.GET_MODEL_STATE;
            }
            //Запрос на получение всех данных для отображения
            if(action.equals(Constants.GET_MODEL_STATE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                Element messageList = new Element("messageList");
                XMLHelper.addElement(messageList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getMessages()));
                session.setAttribute(Constants.MESSAGE_LIST, XMLHelper.outputXML(messageList));

                getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
            }

            /*User section*/
            /*Асинхронное получение данных о user-е*/
            else if(action.equals(Constants.GET_USER)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    int id = Integer.parseInt((String) request.getParameter(Constants.ID));
                    String userXML = XMLHelper.outputXML(forumDAO.getUserById(id).toXMLElement());
                    out.print(userXML);
                }
            } 
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_USER)) {
                session.setAttribute(Constants.USER, null);
                getServletContext().getRequestDispatcher("/editUser.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление user-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_USER)) {
                User user = parseUser(request);
                forumDAO.addUser(user);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_USER)) {
                int id = Integer.parseInt(request.getParameter(Constants.ID));
                User user = forumDAO.getUserById(id);
                String userXML =  XMLHelper.outputXML(user.toXMLElement());
                session.setAttribute(Constants.USER, userXML);
                getServletContext().getRequestDispatcher("/editUser.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену user-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_USER)) {
                User user = parseUser(request);
                int id = (Integer) session.getAttribute(Constants.ID);
                user.setId(id);
                //на всякий случай...
                session.setAttribute(Constants.ID, null);
                forumDAO.replaceUser(user);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_USER)) {
                int id = Integer.parseInt(request.getParameter(Constants.ID));
                forumDAO.deleteUserById(id);
                routeToMainPage(request, response);
            } else if(action.equals(Constants.SEARCH_USER)) {
                String searchParameter = request.getParameter(Constants.USER_SEARCH_PARAMETER);
                Collection<User> users = forumDAO.findUsersByName(searchParameter);
                Element usersElement = new Element("Users");
                XMLHelper.addElement(usersElement, Constants.VALUES_XML_TAG, users);
                session.setAttribute(Constants.FOUND_USERS, XMLHelper.outputXML(usersElement));
                getServletContext().getRequestDispatcher("/userSearchResult.jsp").forward(request, response);
            }

            /*Thread section*/
            /*Асинхронное получение данных о thread-е*/
            else if(action.equals(Constants.GET_THREAD)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    int id = Integer.parseInt((String) request.getParameter(Constants.ID));
                    String threadXML = XMLHelper.outputXML(forumDAO.getThreadById(id).toXMLElement());
                    out.print(threadXML);
                }
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_THREAD)) {
                session.setAttribute(Constants.THREAD, null);
                getServletContext().getRequestDispatcher("/editThread.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление thread-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_THREAD)) {
                ru.ssau.entities.Thread thread = parseThread(request);
                forumDAO.addThread(thread);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_THREAD)) {
                int id = Integer.parseInt(request.getParameter(Constants.ID));
                ru.ssau.entities.Thread thread = forumDAO.getThreadById(id);
                String threadXML =  XMLHelper.outputXML(thread.toXMLElement());
                session.setAttribute(Constants.THREAD, threadXML);
                getServletContext().getRequestDispatcher("/editThread.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену thread-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_THREAD)) {
                ru.ssau.entities.Thread thread = parseThread(request);
                int id = (Integer) session.getAttribute(Constants.ID);
                thread.setId(id);
                session.setAttribute(Constants.ID, null);
                forumDAO.replaceThread(thread);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_THREAD)) {
                int id = Integer.parseInt(request.getParameter(Constants.ID));
                forumDAO.deleteThreadById(id);
                routeToMainPage(request, response);
            } else if(action.equals(Constants.SEARCH_THREAD)) {
                String searchParameter = request.getParameter(Constants.THREAD_SEARCH_PARAMETER);
                Collection<ru.ssau.entities.Thread> threads = forumDAO.findThreadsByCaption(searchParameter);
                Element threadsElement = new Element("Threads");
                XMLHelper.addElement(threadsElement, Constants.VALUES_XML_TAG, threads);
                session.setAttribute(Constants.FOUND_THREADS, XMLHelper.outputXML(threadsElement));
                getServletContext().getRequestDispatcher("/threadSearchResult.jsp").forward(request, response);
            }

            /*Message section*/
            /*Асинхронное получение данных о message-е*/
            else if(action.equals(Constants.GET_MESSAGE)) {
                String value = (String) request.getParameter(Constants.ID);
                if(!StringUtils.isEmpty(value)) {
                    int id = Integer.parseInt((String) request.getParameter(Constants.ID));
                    String messageXML = XMLHelper.outputXML(forumDAO.getMessageById(id).toXMLElement());
                    out.print(messageXML);
                }
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.ADD_MESSAGE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                session.setAttribute(Constants.MESSAGE, null);
                getServletContext().getRequestDispatcher("/editMessage.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет добавление user-а в хранилище данных*/
            else if(action.equals(Constants.DO_ADD_MESSAGE)) {
                Message message = parseMessage(request);
                forumDAO.addMessage(message);
                routeToMainPage(request, response);
            }
            /*Этот запрос направляет пользователя на страницу редактирования*/
            else if(action.equals(Constants.EDIT_MESSAGE)) {
                Element userList = new Element("userList");
                XMLHelper.addElement(userList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getUsers()));
                session.setAttribute(Constants.USER_LIST, XMLHelper.outputXML(userList));

                Element threadList = new Element("threadList");
                XMLHelper.addElement(threadList, Constants.VALUES_XML_TAG, null,
                        StringIntegerPair.toStringIntegerPairArray(forumDAO.getThreads()));
                session.setAttribute(Constants.THREAD_LIST, XMLHelper.outputXML(threadList));

                int id = Integer.parseInt(request.getParameter(Constants.ID));
                Message message = forumDAO.getMessageById(id);
                String messageXML =  XMLHelper.outputXML(message.toXMLElement());
                session.setAttribute(Constants.MESSAGE, messageXML);
                getServletContext().getRequestDispatcher("/editMessage.jsp").forward(request, response);
            }
            /*Этот запрос осуществляет замену user-а в хранилище данных*/
            else if(action.equals(Constants.DO_EDIT_MESSAGE)) {
                Message message = parseMessage(request);
                int id = (Integer) session.getAttribute(Constants.ID);
                message.setId(id);
                session.setAttribute(Constants.ID, null);
                forumDAO.replaceMessage(message);
                routeToMainPage(request, response);
            }
            else if(action.equals(Constants.DELETE_MESSAGE)) {
                int id = Integer.parseInt(request.getParameter(Constants.ID));
                forumDAO.deleteMessageById(id);
                routeToMainPage(request, response);
            } else if(action.equals(Constants.SEARCH_MESSAGE)) {
                String searchParameter = request.getParameter(Constants.MESSAGE_SEARCH_PARAMETER);
                Collection<Message> messages = forumDAO.findMessagesByText(searchParameter);
                Element threadsElement = new Element("Messages");
                XMLHelper.addElement(threadsElement, Constants.VALUES_XML_TAG, messages);
                session.setAttribute(Constants.FOUND_MESSAGES, XMLHelper.outputXML(threadsElement));
                getServletContext().getRequestDispatcher("/messageSearchResult.jsp").forward(request, response);
            }


        } catch(DataAccessException e) {
            e.printStackTrace();
            session.setAttribute("message", e.getMessage());
            getServletContext().getRequestDispatcher("/sError.jsp").forward(request, response);
        } catch(InvalidValueException e) {
            e.printStackTrace();
            session.setAttribute("message", e.getMessage());
            getServletContext().getRequestDispatcher("/sError.jsp").forward(request, response);
        } finally {
            out.close();
        }
    }

    private void routeToMainPage(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String forwardString = String.format("/request?%s=%s", Constants.ACTION, Constants.GET_MODEL_STATE);
        getServletContext().getRequestDispatcher(forwardString).forward(request, response);
    }

    private User parseUser(HttpServletRequest req) throws InvalidValueException {
        try {
            String login = req.getParameter(Constants.LOGIN);
            if(StringUtils.isEmpty(login)) {
                throw new InvalidValueException("User Login is empty");
            }
            String password = req.getParameter(Constants.PASSWORD);
            if(StringUtils.isEmpty(password)) {
                throw new InvalidValueException("User Password is empty");
            }
            AuthorizationInfo authInfo = new AuthorizationInfo(login, password);
            User user = new User();
            user.setAuthorizationInfo(authInfo);

            String setCurrentDateOption = req.getParameter(Constants.DATE_SET_OPTION);
            if(setCurrentDateOption == null) {
                user.setRegistrationDate(parseDate(req));
            } else {
                user.setRegistrationDate(new Date());
            }
            String info = req.getParameter(Constants.INFO);
            user.setInfo(info);
            String name = req.getParameter(Constants.NAME);
            if(StringUtils.isEmpty(name)) {
                throw new InvalidValueException("User Name is empty");
            }
            user.setName(name);
            return user;
        } catch(NumberFormatException e) {
            throw new InvalidValueException(e);
        }
    }

    private ru.ssau.entities.Thread parseThread(HttpServletRequest req) throws InvalidValueException {
        String caption = req.getParameter(Constants.CAPTION);
        if(StringUtils.isEmpty(caption)) {
            throw new InvalidValueException("Thread Caption is empty");
        }
        ru.ssau.entities.Thread thread = new ru.ssau.entities.Thread();
        thread.setCaption(caption);
        return thread;
    }

    private Message parseMessage(HttpServletRequest req) throws InvalidValueException {
        try {
            String caption = req.getParameter(Constants.CAPTION);
            if(StringUtils.isEmpty(caption)) {
                throw new InvalidValueException("Message Caption is empty");
            }
            Message message = new Message();
            message.setCaption(caption);

            int userId = Integer.parseInt(req.getParameter(Constants.USER_ID_IN_MESSAGE));
            message.setUserId(userId);

            int threadId = Integer.parseInt(req.getParameter(Constants.THREAD_ID_IN_MESSAGE));
            message.setThreadId(threadId);

            String text = req.getParameter(Constants.TEXT);
            if(StringUtils.isEmpty(text)) {
                throw new InvalidValueException("Message Text is empty");
            }
            message.setText(text);

            String setCurrentDateOption = req.getParameter(Constants.DATE_SET_OPTION);
            if(setCurrentDateOption == null) {
                message.setPostDate(parseDate(req));
            } else {
                message.setPostDate(new Date());
            }
            return message;
        } catch(NumberFormatException e) {
            throw new InvalidValueException(e);
        }
    }

    private Date parseDate(HttpServletRequest req) throws InvalidValueException {
        int year = Integer.parseInt(req.getParameter(Constants.YEAR));
        int month = Integer.parseInt(req.getParameter(Constants.MONTH));
        int day = Integer.parseInt(req.getParameter(Constants.DAY));
        int hours = Integer.parseInt(req.getParameter(Constants.HOURS));
        int minutes = Integer.parseInt(req.getParameter(Constants.MINUTES));
        int seconds = Integer.parseInt(req.getParameter(Constants.SECONDS));
        String field = checkDateFields(year, month, day, hours, minutes, seconds);
        if(field != null) {
            throw new InvalidValueException("Invalid date: " + field);
        }
        Calendar c = new GregorianCalendar(year, month-1, day, hours, minutes, seconds);
        return c.getTime();
    }

    /**
     * Проверяет входные параметры на принадлежность допустимым интервалам
     * @param year год.
     * @param month месяц.
     * @param day день.
     * @param hours часы.
     * @param minutes минуты.
     * @param seconds секунды.
     * @return Имя переменной, значение которой не принадлежит допустимому интервалу.
     * Если все значения валидны, возвращает null.
     */
    private String checkDateFields(int year, int month, int day, int hours, int minutes, int seconds) {
        if(year < 1) return "Year";
        if(month < 1 || month > 12) return "Month";
        if(day < 1) return "Day";
        if(hours > 23 || hours < 0) return "Hours";
        if(minutes > 59 || minutes < 0) return "Minutes";
        if(seconds > 59 || seconds < 0) return "Seconds";


        boolean isDayCorrect = true;

        if(month == 1) {
            isDayCorrect &= (day <= 31);
        } else if(month == 2) {
            if(year % 4 == 0) {
                isDayCorrect &= (day <= 29);
            } else {
                isDayCorrect &= (day <= 28);
            }
        } else if(month == 3) {
            isDayCorrect &= (day <= 31);
        } else if(month == 4) {
            isDayCorrect &= (day <= 30);
        } else if(month == 5) {
            isDayCorrect &= (day <= 31);
        } else if(month == 6) {
            isDayCorrect &= (day <= 30);
        } else if(month == 7) {
            isDayCorrect &= (day <= 31);
        } else if(month == 8) {
            isDayCorrect &= (day <= 31);
        } else if(month == 9) {
            isDayCorrect &= (day <= 30);
        } else if(month == 10) {
            isDayCorrect &= (day <= 31);
        } else if(month == 11) {
            isDayCorrect &= (day <= 30);
        } else if(month == 12) {
            isDayCorrect &= (day <= 31);
        }

        if(isDayCorrect) {
            return null;
        } else {
            return "Day";
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    } 

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "This servlets handles requests to change some forum data";
    }// </editor-fold>

}
